package com.stardata.liangshisq.tcslmon.service.impl;

import com.stardata.liangshisq.tcslmon.entity.KcKsc;
import com.stardata.liangshisq.tcslmon.entity.WindowItemQueueVo;
import com.stardata.liangshisq.tcslmon.service.KcKscService;
import com.stardata.liangshisq.tcslmon.service.SysParameterService;
import com.stardata.liangshisq.tcslmon.util.BeanUtil;
import com.stardata.liangshisq.tcslmon.util.CommonConstants;
import com.stardata.liangshisq.tcslmon.util.JSONUtil;
import com.stardata.liangshisq.tcslmon.util.RRException;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author Samson Shu
 * @email shush@stardata.top
 * @date 2020/12/14 11:21
 */
@Getter
public class KitchenQueueUtil {

  private final SysParameterService parameterService;
  private final KcKscService kcKscService ;
  private final List<Long> jumpOrderIds = new ArrayList<>();
  private final List<Long> jumpItemIds = new ArrayList<>();

  private final List<String> prepareWindowMames = Arrays.asList("cookOrFryItemWindow", "normalItemWindow"); //配菜窗口队列名称（是个数组，支持配菜窗口有多个）
  private final List<Integer> prepareWindowSizes = Arrays.asList(3, 4); //配菜窗口最多行数（是个数组，支持配菜窗口有多个）
  private final List<Integer> prepareMergeCounts = Arrays.asList(2,2); //配菜窗口订单合并数（是个数组，支持配菜窗口有多个）

  private final List<List<Long>> prepareWindowClassIds = new ArrayList<>(); //配菜窗口对应的小类ID列表
  private int prioritySize = 3;  //配菜中订单优先轮流订单数（默认3）
  private int maxPreparingSize = 5; //最大配菜中订单数（默认5）
  private int maxJumpOrderCount = 1; //最大允许插队订单数（默认1）
  private int maxJumpItemCount = 1; //最大允许插队菜品数（默认2）
  private int onlineLeadMinutes = 17; //线上订单提前排队分钟数（默认45）
  private int offlineLeadMinutes = 17; //线下订单提前排队分钟数（默认30）
  private int retailLeadMinutes = 17; //零售订单提前排队分钟数（默认30）
  private int maxCookedItemCount = 2; //成品熟食最多排队行数

  private KitchenQueueUtil(){
    this.parameterService = BeanUtil.getBean(SysParameterService.class);
    this.kcKscService = BeanUtil.getBean(KcKscService.class);

    try {
      this.maxPreparingSize = Integer.parseInt(parameterService.getByCode("maxPreparingSize"));
    } catch (Exception ignore) {}
    try {
      List<Long> classIds = new ArrayList<>();
      List<? extends List> aList = parameterService.getListByCode("prepareWindowClassIds", classIds.getClass());
      this.prepareWindowClassIds.addAll((List<List<Long>>)aList);
    } catch (Exception ignore) {}
    try {
      List<String> aList = parameterService.getListByCode("prepareWindowMames", String.class);
      this.prepareWindowMames.clear();
      this.prepareWindowMames.addAll(aList);
    } catch (Exception ignore) {}
    try {
      List<Integer> aList = parameterService.getListByCode("prepareWindowSizes", Integer.class);
      this.prepareWindowSizes.clear();
      this.prepareWindowSizes.addAll(aList);
    } catch (Exception ignore) {}
    try {
      List<Integer> aList = parameterService.getListByCode("prepareMergeCounts", Integer.class);
      this.prepareMergeCounts.clear();
      this.prepareMergeCounts.addAll(aList);
    } catch (Exception ignore) {}
    if ((prepareWindowClassIds.size() != prepareWindowMames.size()) ||
        (prepareWindowClassIds.size() != prepareWindowSizes.size()) ||
        (prepareWindowClassIds.size() != prepareMergeCounts.size())) {
      throw new RRException("ERROR ==> 系统参数配置错误：配菜窗口类别编码、配菜窗口队列名称、" +
          "配菜窗口最大记录数、配菜最大合并数 4者长度不一致！");
    }
    try {
      this.prioritySize = Integer.parseInt(parameterService.getByCode("prioritySize"));
    }catch (Exception ignore) {}
    try {
      this.maxJumpOrderCount = Integer.parseInt(parameterService.getByCode("maxJumpOrderCount"));
    } catch (Exception ignore) {}
    try {
      this.maxJumpItemCount = Integer.parseInt(parameterService.getByCode("maxJumpItemCount"));
    } catch (Exception ignore) {}
    try {
      this.onlineLeadMinutes = Integer.parseInt(parameterService.getByCode("onlineLeadMinutes"));
    } catch (Exception ignore) {}
    try {
      this.offlineLeadMinutes = Integer.parseInt(parameterService.getByCode("offlineLeadMinutes"));
    } catch (Exception ignore) {}
    try {
      this.retailLeadMinutes = Integer.parseInt(parameterService.getByCode("retailLeadMinutes"));
    } catch (Exception ignore) {}
    try {
      this.maxCookedItemCount = Integer.parseInt(parameterService.getByCode("maxCookedItemCount"));
    } catch (Exception ignore) {}

    this.jumpOrderIds.addAll(parameterService.getListByCode("jumpOrderIds", Long.class));
    this.jumpItemIds.addAll(parameterService.getListByCode("jumpItemIds", Long.class));

  }


  private static class SingletonClassInstance{
    private static final KitchenQueueUtil instance = new KitchenQueueUtil();
  }


  public static KitchenQueueUtil getInstance(){
    return SingletonClassInstance.instance;
  }


  /**
   * 判断配菜窗口是否已满
   * @param windowList 配菜窗口列表
   * @return 已满则返回true，否则false
   */
  public boolean windowIsFulled(@NotNull List<WindowItemQueueVo> windowList, int windowIndex) {
    int windowSize = prepareWindowSizes.get(windowIndex);
    int mergeCount = prepareMergeCounts.get(windowIndex);
    if (windowList.size() < windowSize) return false;

    for (WindowItemQueueVo item : windowList) {
      if (item.getKscIds().size() < mergeCount) return false;
    }
    return true;
  }

  /**
   * 将后厨制作菜品项合入配菜窗口列表
   * @param windowList 配菜窗口列表
   * @param kcKscToMerge 指定的后厨制作菜品项
   * @param windowSize 配菜窗口最大行数
   * @param mergeCount 配菜窗口每行最大合并订单数
   * @return 是否合并成功
   */
  public boolean mergeKcKscItemToWindowList(@NotNull List<WindowItemQueueVo> windowList, KcKsc kcKscToMerge,
                                                   int windowSize, int mergeCount) {

    //判断新菜品与windowList菜品是否有重复且重复数<N
    WindowItemQueueVo itemWindowToMerge = null;
    for (WindowItemQueueVo itemWindow : windowList) {
      //如果被合并的菜品，其实已经存在于 windowList，则直接返回 true (表示合并成功，但其实什么也没做)
      if (itemWindow.getKscIds().contains(kcKscToMerge.getId())) return true;
      if (kcKscToMerge.getIsTempItem()) {
        if (itemWindow.getItemId().equals(kcKscToMerge.getItemId()) &&
            itemWindow.getItemName().equals(kcKscToMerge.getTempItemName()) &&
            itemWindow.getIsTempItem() &&
            itemWindow.getCount() + kcKscToMerge.getLastQty().intValue() <= mergeCount) {
          itemWindowToMerge = itemWindow;
          break;
        }
      } else {
        if (itemWindow.getItemId().equals(kcKscToMerge.getItemId()) &&
            itemWindow.getCount() + kcKscToMerge.getLastQty().intValue() <= mergeCount) {
          itemWindowToMerge = itemWindow;
          break;
        }
      }
    }

    //如果有重复，则合并到 windowList 的重复行，否则追加到 windowList 的尾部
    if (itemWindowToMerge != null) {
      itemWindowToMerge.getKscIds().add(kcKscToMerge.getId());
      itemWindowToMerge.getBsIds().add(kcKscToMerge.getBsId());
      itemWindowToMerge.setCount(itemWindowToMerge.getCount() + kcKscToMerge.getLastQty().intValue());
      //设置制作窗口菜品是否插队的标记
      synchronized (jumpItemIds) {
        itemWindowToMerge.getIsJumped().add(jumpItemIds.contains(kcKscToMerge.getId()));
      }
      return true;
    } else if (windowList.size() < windowSize){
      itemWindowToMerge = new WindowItemQueueVo();
      windowList.add(itemWindowToMerge);
      itemWindowToMerge.setItemId(kcKscToMerge.getItemId());
      boolean isTempItem = kcKscToMerge.getIsTempItem();
      itemWindowToMerge.setIsTempItem(isTempItem);
      if (isTempItem) {
        itemWindowToMerge.setItemName(kcKscToMerge.getTempItemName());
      } else {
        itemWindowToMerge.setItemName(kcKscToMerge.getArchItem().getName());
      }
      itemWindowToMerge.setBsIds(new ArrayList<>());
      itemWindowToMerge.getBsIds().add(kcKscToMerge.getBsId());
      itemWindowToMerge.setKscIds(new ArrayList<>());
      itemWindowToMerge.getKscIds().add(kcKscToMerge.getId());
      itemWindowToMerge.setCount(kcKscToMerge.getLastQty().intValue());
      //设置制作窗口菜品是否插队的标记
      itemWindowToMerge.setIsJumped(new ArrayList<>());
      synchronized (jumpItemIds) {
        itemWindowToMerge.getIsJumped().add(jumpItemIds.contains(kcKscToMerge.getId()));
        return true;
      }
    }
    return false;
  }

  /**
   * 将后厨制作菜品项列表合入配菜窗口列表
   * @param windowList 配菜窗口列表
   * @param kcKscList 指定的后厨制作菜品项列表
   * @param windowIndex 配菜窗口序号
   * @return 配菜窗口列表是否已满
   */
  public boolean mergeKcKscListToWindowList(@NotNull List<WindowItemQueueVo> windowList,
                                                   @NotNull List<KcKsc> kcKscList, int windowIndex) {
    if (kcKscList.size()<1) return false;

    int windowSize = prepareWindowSizes.get(windowIndex);
    int mergeCount = prepareMergeCounts.get(windowIndex);

    //先将所有待合并菜品列表的 bs_id 取出来，以便后续使用
    List<Long> toMergeBsIds = new ArrayList<>();
    for (KcKsc kcKsc : kcKscList) {
      if (!toMergeBsIds.contains(kcKsc.getBsId())) {
        toMergeBsIds.add(kcKsc.getBsId());
      }
    }

    //建立遍历列表
    List<KcKsc> listToWalk = new ArrayList<>(kcKscList);

    boolean finished = false;
    int index = 0;
    while (!finished) {
      //从 toMergeList 按订单轮流取得下一个菜品 （最多把所有订单遍历到，还找不到表示 toMergeList 已无项目可用，循环可以结束了）
      int tryCount = 0;
      KcKsc kcKscToMerge = null;
      while (tryCount < toMergeBsIds.size()) {
        Long curBsId = toMergeBsIds.get(index % toMergeBsIds.size());
        for (KcKsc kcKsc : listToWalk) {
          //如果是当前订单下的菜品、且该菜品不是煮炸类、不是成品熟食，则作为待合并菜品
          if (kcKsc.getBsId().equals(curBsId)) {
            kcKscToMerge = kcKsc;
            break;
          }
        }
        index++;
        if (kcKscToMerge == null) {
          tryCount++;
        } else {
          break;
        }
      }
      if (kcKscToMerge == null) break;

      if (mergeKcKscItemToWindowList(windowList, kcKscToMerge, windowSize, mergeCount)) {
        //将已经被合并到 windowList 的菜品记录，从 toMergeList 删除
        kcKscList.remove(kcKscToMerge);
      }
      //将已经遍历到的菜品对象从遍历列表删除
      listToWalk.remove(kcKscToMerge);

      //判断 windowList 是否已满，如果满了则可以退出了
      finished = windowIsFulled(windowList, windowIndex);
    }
    return finished;
  }

  /**
   * 获取所有插队订单id列表
   * @return 订单插队id列表
   */
  public List<Long> getJumpOrderIds() {
    KitchenQueueUtil instance = KitchenQueueUtil.getInstance();
    return instance.jumpOrderIds;
  }

  /**
   * 将订单id插队到"订单插队列表"
   * @param bsId 待插队的订单id
   */
  public void addToJumpOrders(@NotNull Long bsId) {
    synchronized (jumpOrderIds) {
      if (!jumpOrderIds.contains(bsId)) {
        jumpOrderIds.add(bsId);
        parameterService.saveByCode("jumpOrderIds", JSONUtil.toJSONString(jumpOrderIds));

        List<KcKsc> kcKscs = kcKscService.selectByBsId(bsId);
        List<Long> kcKscIds = new ArrayList<>();
        for (KcKsc kcKsc : kcKscs) {
          if (kcKsc.getKitchenFlg().equals(CommonConstants.KitchenFlg.TO_PREPARE)) {
            kcKscIds.add(kcKsc.getId());
          }
        }
        addAllToJumpItems(kcKscIds);
      }
    }
  }

  /**
   * 从插队订单id列表中删除指定订单id
   * @param bsId 待删除的订单id
   */
  public void removeFromJumpOrders(@NotNull Long bsId) {
    synchronized (jumpOrderIds) {
      if (jumpOrderIds.remove(bsId)) {
        parameterService.saveByCode("jumpOrderIds", JSONUtil.toJSONString(jumpOrderIds));

        List<KcKsc> kcKscs = kcKscService.selectByBsId(bsId);
        List<Long> kcKscIds = new ArrayList<>();
        for (KcKsc kcKsc : kcKscs) {
          if (kcKsc.getKitchenFlg().equals(CommonConstants.KitchenFlg.TO_PREPARE)) {
            kcKscIds.add(kcKsc.getId());
          }
        }
        removeAllFromJumpItems(kcKscIds);
      }
    }
  }

  /**
   * 将单个菜品项后厨ksKscId插队到"菜品项插队列表"
   * @param kcKscId 待插队的菜品项后厨kcKscId
   */
  public void addToJumpItems(@NotNull Long kcKscId) {
    synchronized (jumpItemIds) {
      if (!jumpItemIds.contains(kcKscId)) {
        jumpItemIds.add(kcKscId);
        parameterService.saveByCode("jumpItemIds", JSONUtil.toJSONString(jumpItemIds));
      }
    }
  }

  /**
   * 将多个菜品项后厨ksKscIds插队到"菜品项插队列表"
   * @param kcKscIds 待插队的菜品项后厨kcKscId列表
   */
  public void addAllToJumpItems(@NotNull List<Long> kcKscIds) {
    synchronized (jumpItemIds) {
      if (jumpItemIds.addAll(kcKscIds)) {
        parameterService.saveByCode("jumpItemIds", JSONUtil.toJSONString(jumpItemIds));
      }
    }
  }

  /**
   * 从"菜品项插队列表"删除多个后厨ksKscIds
   * @param kcKscIds 待删除的后厨菜品项kcKscIds
   */
  public void removeAllFromJumpItems(@NotNull List<Long> kcKscIds) {
    synchronized (jumpItemIds) {
      if (jumpItemIds.removeAll(kcKscIds)) {
        parameterService.saveByCode("jumpItemIds", JSONUtil.toJSONString(jumpItemIds));
      }
    }
  }

  /**
   * 从"菜品项插队列表"删除单品插队的记录
   * @param kcKscIds 待删除的后厨菜品项kcKscIds
   */
  public void removeSingleJumpItems(@NotNull List<Long> kcKscIds) {
    List<KcKsc> kcKscs = kcKscService.selectByKcKscIds(kcKscIds);
    List<Long> singleKcKscIds = new ArrayList<>();
    for (KcKsc kcKsc : kcKscs) {
      if (!jumpOrderIds.contains((kcKsc.getBsId()))) {
        singleKcKscIds.add(kcKsc.getId());
      }
    }
    if (singleKcKscIds.size()>0) {
      removeAllFromJumpItems(singleKcKscIds);
    }
  }


  /**
   * 从配菜制作窗口中，删除指定的多个菜品项后厨kcKscId
   * @param windowList 配菜窗口列表
   * @param kcKscIds 待删除的后厨菜品项kcKscIds
   */
  public boolean removeKcKscFromWindowList(@NotNull List<WindowItemQueueVo> windowList, @NotNull List<Long> kcKscIds) {
    boolean changed = false;
    for (int i = windowList.size() - 1; i >= 0; i--) {
      WindowItemQueueVo windowItem = windowList.get(i);
      for (int j = windowItem.getKscIds().size() - 1; j >= 0; j--) {
        Long itemKsKscId = windowItem.getKscIds().get(j);
        int idx = kcKscIds.indexOf(itemKsKscId);
        if (idx > -1) {
          windowItem.getKscIds().remove(j);
          windowItem.getBsIds().remove(j);
          windowItem.getIsJumped().remove(j);
          windowItem.setCount(windowItem.getCount()-1);
          changed = true;
        }
      }
      if (windowItem.getKscIds().size()<1) {
        windowList.remove(i);
      }
    }
    return changed;
  }

}
